<template>
  <div class="layout-root" :class="{ 'standalone-poster': isStandalonePoster }">
    <aside v-if="!isStandalonePoster" class="sidebar">
      <div class="brand">
        <img class="brand-logo" src="@/assets/logos/logo.png" alt="Logo" />
        <span class="brand-text">再生建材成品报价</span>
      </div>
      <nav class="nav-group">
  <RouterLink to="/" class="nav-item root" active-class="active" exact>报价查询</RouterLink>
        <!-- 手风琴：产品类型(二级) -> 材料分类(三级)  -->
        <div class="tree-nav-wrapper">
          <div class="tree-root nav-item root" :class="{active: isBomRoute}" @click="goBomRoot" role="button" tabindex="0" @keydown.enter.prevent="goBomRoot"> 
            <span class="tree-label single">BOM条目</span>
          </div>
          <transition
            name="menu-fade-slide"
            @before-enter="beforeEnterRootList"
            @enter="enterRootList"
            @after-enter="afterEnterRootList"
            @before-leave="beforeLeaveRootList"
            @leave="leaveRootList"
          >
          <ul v-if="isBomRoute" class="tree-level tree-lvl-1 root-second" role="tree" aria-label="产品类型树">
            <li v-for="p in productTypesNoAll" :key="p" class="tree-node" role="treeitem" :aria-expanded="expandedProductType===p">
              <div
                class="tree-item"
                :class="{ opened: expandedProductType===p, selected: currentProductType===p }"
                @click="toggleProductTree(p)"
                tabindex="0"
                @keydown.enter.prevent="toggleProductTree(p)"
              >
                <span class="tree-caret-icon" :aria-label="expandedProductType===p? '折叠'+p : '展开'+p" @click.stop="toggleProductTree(p)">
                  <svg viewBox="0 0 20 20" class="caret-icon"><path d="M6 8l4 4 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
                </span>
                <span class="tree-label">{{ p }}</span>
              </div>
              <transition
                @before-enter="beforeEnter"
                @enter="enter"
                @after-enter="afterEnter"
                @before-leave="beforeLeave"
                @leave="leave"
                @after-leave="afterLeave"
              >
                <ul v-show="expandedProductType===p" class="tree-level tree-lvl-2" role="group">
                  <li
                    v-for="c in (bomProductTypeCategories[p] || [])"
                    :key="c"
                    class="tree-node leaf"
                    :class="{ active: currentProductType===p && currentCategoryOrAll===c }"
                  >
                    <RouterLink
                      :to="materialLink(p,c)"
                      class="tree-leaf-link"
                      :aria-current="(currentProductType===p && currentCategoryOrAll===c) ? 'page' : undefined"
                    >
                      <span class="leaf-dot" aria-hidden="true"></span>
                      <span>{{ c }}</span>
                    </RouterLink>
                  </li>
                  <li v-if="expandedProductType===p && !bomProductTypeCategories[p]" class="tree-node leaf" style="padding:.3rem .4rem .6rem 0; color:#9fb9e8; font-size:12px;">加载中...</li>
                </ul>
              </transition>
            </li>
          </ul>
          </transition>
        </div>
        <!-- 产品明细：只需要二级（产品类型） -->
        <div class="tree-nav-wrapper">
          <div class="tree-root nav-item root" :class="{active: isSkuRoute}" @click="goSkuRoot" role="button" tabindex="0" @keydown.enter.prevent="goSkuRoot"> 
            <span class="tree-label single">产品明细</span>
          </div>
          <transition
            name="menu-fade-slide"
            @before-enter="beforeEnterRootList"
            @enter="enterRootList"
            @after-enter="afterEnterRootList"
            @before-leave="beforeLeaveRootList"
            @leave="leaveRootList"
          >
          <ul v-if="isSkuRoute" class="tree-level tree-lvl-1 root-second" role="tree" aria-label="产品类型树-产品明细">
            <li v-for="p in productTypesForSku" :key="p" class="tree-node" role="treeitem">
              <div
                class="tree-item"
                :class="{ selected: expandedSkuProductType===p }"
                @click="selectSkuProductType(p)"
                tabindex="0"
                @keydown.enter.prevent="selectSkuProductType(p)"
              >
                <span class="tree-caret-spacer" aria-hidden="true"></span>
                <span class="tree-label">{{ p }}</span>
              </div>
            </li>
          </ul>
          </transition>
        </div>

        <!-- 包装方式 -->
        <RouterLink to="/package-method" class="nav-item root" active-class="active">包装方式</RouterLink>
      </nav>
      <footer class="sidebar-foot">© 2025 英科再生</footer>
    </aside>
    <main class="main-panel" :class="{ 'no-chrome': isStandalonePoster }">
      <header v-if="!isStandalonePoster" class="page-topbar">
        <h1 class="page-title">
          <img class="page-logo" src="@/assets/logos/logo.png" alt="Logo" />
          <span>{{ currentTitle }}</span>
        </h1>
      </header>
      <section class="view-wrapper" :class="{ 'poster-wrapper': isStandalonePoster }">
        <RouterView />
      </section>
    </main>
  </div>
</template>

<script setup lang="ts">
import { onMounted, watch, ref, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const route = useRoute();
const router = useRouter();
const currentTitle = ref('');
// 是否报价海报独立页
const isStandalonePoster = computed(()=> route.meta && (route.meta as any).standalonePoster === true);

// 标题映射：保持简洁；可在需要时扩展更多路由标题
const titleMap: Record<string, string> = { home: '报价查询', 'package-method': '包装方式', 'bom-simple-entries': 'BOM条目', 'sku-product-details': '产品明细' };

// ==== 动态菜单（替换原写死 productTypeTree） =====================================
import { getBomProducts, getBomProductClasses, getSkuProducts } from './services/menu';

// BOM 产品类型列表（一级）
const bomProductTypes = ref<string[]>([]);
// SKU 产品类型列表（用于“产品明细”二级）
const skuProductTypes = ref<string[]>([]);
// 各产品类型对应的材料分类（二级 -> 三级）缓存
const bomProductTypeCategories = ref<Record<string, string[]>>({});
// 加载状态（避免重复请求）
const loadingBomProducts = ref(false);
const loadingSkuProducts = ref(false);
const loadingCategories = ref<Record<string, boolean>>({});

async function ensureBomProducts() {
  if (bomProductTypes.value.length || loadingBomProducts.value) return;
  loadingBomProducts.value = true;
  try {
    const list = await getBomProducts();
    // 保持与旧逻辑“通用”存在的兼容：如果后端没返回且业务仍需要就追加。
    const hasGeneral = list.includes('通用');
    bomProductTypes.value = hasGeneral ? list : ['通用', ...list];
  } catch (e) { console.error('加载 BOM 产品类型失败', e); } finally { loadingBomProducts.value = false; }
}

async function ensureSkuProducts() {
  if (skuProductTypes.value.length || loadingSkuProducts.value) return;
  loadingSkuProducts.value = true;
  try {
    const list = await getSkuProducts();
    // 旧逻辑过滤“通用”
    skuProductTypes.value = list.filter(p => p !== '通用');
  } catch (e) { console.error('加载 SKU 产品类型失败', e); } finally { loadingSkuProducts.value = false; }
}

async function ensureCategories(productType: string) {
  if (!productType || bomProductTypeCategories.value[productType] || loadingCategories.value[productType]) return;
  loadingCategories.value[productType] = true;
  try {
    const list = await getBomProductClasses(productType);
    // 统一在前端首位插入“全部”选项，便于保持旧路由逻辑
    bomProductTypeCategories.value[productType] = ['全部', ...list.filter(c => c !== '全部')];
  } catch (e) { console.error('加载产品类型分类失败', productType, e); }
  finally { loadingCategories.value[productType] = false; }
}

// 供模板使用的计算属性（保持变量名尽量接近旧逻辑，减少后续 diff）
const productTypesNoAll = computed(() => bomProductTypes.value);
const productTypesForSku = computed(() => skuProductTypes.value);

// 当前是否处于 BOM 条目 路由
const isBomRoute = computed(() => route.name === 'bom-simple-entries');
// 当前是否处于 产品明细 路由
const isSkuRoute = computed(() => route.name === 'sku-product-details');
const currentProductType = computed(() => (route.query.prod as string) || '');
const currentCategory = computed(() => (route.query.category as string) || '');
const currentCategoryOrAll = computed(() => currentCategory.value || '全部');

// 当前展开的产品类型（树节点）
const expandedProductType = ref<string>('');

function syncExpandedByRoute() {
  if (!isBomRoute.value) { expandedProductType.value = ''; }
  if (isBomRoute.value) {
    const prod = currentProductType.value;
    if (prod && productTypesNoAll.value.includes(prod)) {
      expandedProductType.value = prod;
      // 预拉取分类
      ensureCategories(prod);
    } else expandedProductType.value = '';
  }
  if (!isSkuRoute.value) expandedSkuProductType.value = '';
  if (isSkuRoute.value) {
    const prod = currentProductType.value;
    if (prod && productTypesForSku.value.includes(prod)) expandedSkuProductType.value = prod; else expandedSkuProductType.value = '';
  }
}

function goBomRoot() {
  // 重置展开并跳转到 BOM 根（无 prod/category）
  expandedProductType.value = '';
  navigateMaterial('', '', !isBomRoute.value); // 非当前路由时使用 push
}

// ========== 产品明细 导航（仅两级：根 -> 产品类型） =============
const expandedSkuProductType = ref<string>('');
function goSkuRoot() {
  expandedSkuProductType.value = '';
  navigateSkuProduct('', !isSkuRoute.value);
}
function toggleSkuProductTree(prod: string) {
  const pushFlag = !isSkuRoute.value;
  if (expandedSkuProductType.value === prod) {
    expandedSkuProductType.value = '';
    navigateSkuProduct('', pushFlag);
  } else {
    expandedSkuProductType.value = prod;
    navigateSkuProduct(prod, pushFlag);
  }
}
// 产品明细直接选择（无三级折叠，因此只是选中并跳转）
function selectSkuProductType(prod: string) {
  expandedSkuProductType.value = prod;
  navigateSkuProduct(prod, !isSkuRoute.value);
}
function skuProductLink(prod: string) {
  return { path: '/sku/product-details', query: prod ? { prod } : undefined };
}
function navigateSkuProduct(prod: string, push = false) {
  const query: Record<string,string> = {};
  if (prod) query.prod = prod;
  const navTarget = { path: '/sku/product-details', query };
  if (push || !isSkuRoute.value) router.push(navTarget); else router.replace(navTarget);
}

function toggleProductTree(prod: string) {
  const pushFlag = !isBomRoute.value; // 首次进入使用 push
  if (expandedProductType.value === prod) {
    expandedProductType.value = '';
    navigateMaterial('', '', pushFlag);
  } else {
    expandedProductType.value = prod;
    navigateMaterial(prod, '', pushFlag);
    ensureCategories(prod);
  }
}

function materialLink(prod: string, cat: string) {
  if (cat === '全部') return { path: '/bom-simple/entries', query: { prod } };
  return { path: '/bom-simple/entries', query: { prod, category: cat } };
}

function navigateMaterial(prod: string, cat: string, push = false) {
  const query: Record<string,string> = {};
  if (prod) query.prod = prod;
  if (cat) query.category = cat;
  const navTarget = { path: '/bom-simple/entries', query };
  if (push || !isBomRoute.value) {
    router.push(navTarget);
  } else {
    router.replace(navTarget);
  }
}

// transition hooks：树节点展开/折叠高度动画
function beforeEnter(el: Element) {
  const e = el as HTMLElement; e.style.height = '0'; e.style.opacity = '0';
}
function enter(el: Element) {
  const e = el as HTMLElement; const h = e.scrollHeight; e.style.transition = 'height .32s cubic-bezier(.4,0,.2,1), opacity .28s';
  requestAnimationFrame(() => { e.style.height = h + 'px'; e.style.opacity = '1'; });
}
function afterEnter(el: Element) {
  const e = el as HTMLElement; e.style.height = 'auto'; }
function beforeLeave(el: Element) {
  const e = el as HTMLElement; e.style.height = e.scrollHeight + 'px'; e.style.opacity = '1'; }
function leave(el: Element) {
  const e = el as HTMLElement; e.style.transition = 'height .28s cubic-bezier(.4,0,.2,1), opacity .25s';
  requestAnimationFrame(() => { e.style.height = '0'; e.style.opacity = '0'; });
}
function afterLeave() {
  // no-op
}

// 根级（显示二级菜单列表）展开折叠动画 hooks
function beforeEnterRootList(el: Element) { const e = el as HTMLElement; e.style.height = '0'; e.style.opacity = '0'; }
function enterRootList(el: Element) { const e = el as HTMLElement; const h = e.scrollHeight; e.style.transition = 'height .32s cubic-bezier(.4,0,.2,1), opacity .28s'; requestAnimationFrame(()=>{ e.style.height = h + 'px'; e.style.opacity = '1'; }); }
function afterEnterRootList(el: Element) { const e = el as HTMLElement; e.style.height = 'auto'; }
function beforeLeaveRootList(el: Element) { const e = el as HTMLElement; e.style.height = e.scrollHeight + 'px'; e.style.opacity = '1'; }
function leaveRootList(el: Element) { const e = el as HTMLElement; e.style.transition = 'height .28s cubic-bezier(.4,0,.2,1), opacity .25s'; requestAnimationFrame(()=>{ e.style.height = '0'; e.style.opacity = '0'; }); }

watch(() => route.fullPath, () => {
  syncExpandedByRoute();
});
// 如果在产品明细路由且 prod=通用（已被过滤），自动移除 prod 参数
watch([() => isSkuRoute.value, () => currentProductType.value], ([isSku, prod]) => {
  if (isSku && prod === '通用') {
    navigateSkuProduct('', false);
  }
});
onMounted(async () => {
  // 初始化加载一级菜单
  await Promise.all([ensureBomProducts(), ensureSkuProducts()]);
  // 若路由已有 prod 参数则补充该分类的二级数据
  if (currentProductType.value) ensureCategories(currentProductType.value);
  syncExpandedByRoute();
});

function syncTitle() {
  const name = route.name as string | undefined;
  // 独立海报页使用简洁标题但不展示 topbar
  currentTitle.value = isStandalonePoster.value ? '' : (titleMap[name || 'home'] || '页面');
  document.title = '英科再生-建材成品报价';
  const linkId = 'dynamic-favicon';
  let link = document.head.querySelector<HTMLLinkElement>(`#${linkId}`);
  if (!link) {
    link = document.createElement('link');
    link.id = linkId;
    link.rel = 'icon';
    document.head.appendChild(link);
  }
  // 使用与资源同路径的相对引用
  link.href = new URL('@/assets/logos/logo.png', import.meta.url).href;
}

onMounted(syncTitle);
watch([() => route.name, () => isStandalonePoster.value], syncTitle);
</script>

<style scoped>
* { box-sizing: border-box; }
body,html,#app,.layout-root { height: 100%; }
.layout-root { display: flex; min-height: 100vh; background: #f1f5f9; color: #0f172a; }
.layout-root.standalone-poster { background:#fff; }
.main-panel.no-chrome { padding:0; }
.poster-wrapper { padding:0; display:flex; flex-direction:column; min-height:100vh; }
.layout-root.standalone-poster .poster-wrapper { background:#fff; }
/* 隐藏全局滚动条影响：保持独立页可滚动 */
.layout-root.standalone-poster .view-wrapper { overflow:auto; }
@media (max-width:640px){
  .layout-root.standalone-poster .poster-wrapper { min-height:100dvh; }
}
.sidebar { width: 240px; background: linear-gradient(185deg,#15356d,#1e4fb9 55%,#2a63d8); color: #fff; display: flex; flex-direction: column; padding: 1rem .75rem; position: relative; }
.sidebar:before { content: ""; position: absolute; inset: 0; pointer-events: none; background: radial-gradient(circle at 30% 15%, rgba(255,255,255,.12), transparent 60%); }
.brand { font-size: 16px; font-weight: 600; line-height: 1.3; padding: .55rem .65rem .7rem; border: 1px solid rgba(255,255,255,.18); margin: 0 0 .9rem; display: flex; align-items: center; gap: .65rem; border-radius: 10px; background: rgba(255,255,255,.12); backdrop-filter: blur(4px) saturate(160%); box-shadow: 0 4px 10px -2px rgba(0,0,0,.35), 0 1px 0 0 rgba(255,255,255,.18) inset; }
.brand-logo { width: 40px; height: 40px; object-fit: contain; border-radius: 8px; background: #fff; padding: 4px; box-shadow: 0 2px 4px rgba(0,0,0,.35), 0 0 0 1px rgba(0,0,0,.05); flex-shrink: 0; }
.brand-text { text-shadow: 0 1px 2px rgba(0,0,0,.4); display: inline-block; }
.nav-group { display: flex; flex-direction: column; gap: .25rem; flex: 1; }
.nav-item { padding: .55rem .85rem; border-radius: 8px; color: #e2e8f0; text-decoration: none; font-size: 14px; display: flex; align-items: center; gap: .5rem; transition: background .35s cubic-bezier(.4,0,.2,1), color .18s; position:relative; overflow:hidden; }
.nav-item.root { font-weight:700; letter-spacing:.02em; }
.nav-item::before { content:""; position:absolute; inset:0; background:radial-gradient(circle at 22% 28%, rgba(255,255,255,.28), rgba(255,255,255,0) 65%); opacity:0; transform:scale(.65); transition:opacity .55s ease, transform .6s cubic-bezier(.34,1.56,.4,1); pointer-events:none; }
.nav-item::after { content:""; position:absolute; left:0; top:6px; bottom:6px; width:4px; border-radius:3px; background:linear-gradient(180deg,#60a5fa,#2563eb); transform:scaleY(0); transform-origin:center; transition:transform .4s cubic-bezier(.4,0,.2,1), box-shadow .4s; box-shadow:0 0 0 0 rgba(59,130,246,.0); }
.nav-item:hover { background: rgba(255,255,255,.12); color: #fff; }
.nav-item:hover::before { opacity:1; transform:scale(1); }
.nav-item.active, .nav-item.router-link-active { background: #fff; color: #1d4ed8; font-weight: 600; box-shadow:0 4px 14px -6px rgba(255,255,255,.65),0 2px 6px -2px rgba(0,0,0,.25) inset; }
.nav-item.active::after, .nav-item.router-link-active::after { transform:scaleY(1); box-shadow:0 0 0 2px rgba(255,255,255,.35),0 4px 10px -2px rgba(59,130,246,.55); }
.nav-item:focus-visible { outline:2px solid #93c5fd; outline-offset:2px; }
/* 树形菜单样式 */
/* 高亮策略（统一左侧光条风格）：
  1. 根节点与二级选中都使用同样的左侧主色竖条视觉语言，保持品牌一致。
  2. 去除此前的缩进与独立竖条做法，改为与根节点一致的对齐方式，避免错位带来的跳动感。
  3. 二级选中仍保留白底和阴影，但圆角统一为 8px；根节点与其之间用 4px 间距自然分隔即可。
  4. 三级（材料分类）继续使用橙色点高亮，层级递进清晰。
*/
.tree-nav-wrapper { margin-top:.4rem; }
.tree-root { display:flex; align-items:center; padding:.55rem .75rem; font-size:14px; cursor:pointer; margin-bottom:4px; }
.tree-level { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; }
.tree-lvl-1 { padding:0 0 0 .15rem; gap:.1rem; }
.root-second { margin-top:.15rem; }
.tree-lvl-2 { margin:.2rem 0 .4rem; padding:0 0 0 1.55rem; gap:.1rem; position:relative; }
.tree-lvl-2:before { content:""; position:absolute; left:.55rem; top:.2rem; bottom:.4rem; width:1px; background:linear-gradient(to bottom, rgba(255,255,255,.5), rgba(255,255,255,.15)); }
.tree-node { position:relative; }
.tree-item { display:flex; align-items:center; gap:.3rem; padding:.48rem .6rem .48rem .55rem; color:#dbeafe; font-size:13px; border-radius:8px; cursor:pointer; transition:background .3s,color .25s; }
.tree-item:hover { background:rgba(255,255,255,.12); color:#fff; }
/* 二级节点基础选中态：为防止左侧竖条外溢，开启 overflow:hidden */
.tree-item.opened, .tree-item.selected { background:#fff; color:#1d4ed8; font-weight:600; box-shadow:0 3px 10px -4px rgba(59,130,246,.45); position:relative; border-radius:8px; overflow:hidden; }
/* 二级选中左侧主色竖条（与 nav-item 统一） */
.tree-item.selected::after, .tree-item.opened::after { content:""; position:absolute; left:0; top:4px; bottom:4px; width:3px; border-radius:2px; background:linear-gradient(180deg,#60a5fa,#2563eb); box-shadow:0 0 0 1px rgba(255,255,255,.4),0 2px 6px -2px rgba(59,130,246,.55); }
.tree-item.opened > .tree-caret, .tree-item.selected > .tree-caret { color:#1d4ed8; }
.tree-caret-icon { width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:#8fb0e2; cursor:pointer; transition:color .25s; flex-shrink:0; }
.tree-item:hover > .tree-caret-icon { color:#fff; }
.caret-icon { width:16px; height:16px; transform:rotate(0deg); transition:transform .4s cubic-bezier(.4,0,.2,1); }
.tree-item.opened .caret-icon { transform:rotate(180deg); }
.tree-caret-spacer { width:16px; height:16px; display:inline-block; }
.tree-leaf-link { display:flex; align-items:center; gap:.4rem; padding:.42rem .55rem; border-radius:6px; font-size:13px; color:#dbeafe; text-decoration:none; position:relative; transition:background .3s,color .25s, transform .35s; }
.tree-leaf-link:hover { background:rgba(255,255,255,.15); color:#fff; transform:translateX(2px); }
.leaf-dot { width:6px; height:6px; border-radius:50%; background:#60a5fa; box-shadow:0 0 0 1px rgba(255,255,255,.4); opacity:.65; transition:opacity .25s, transform .35s, background .35s; }
.tree-node.leaf.active > .tree-leaf-link { background:transparent; color:#ffb347; font-weight:600; transform:translateX(0); text-shadow:0 0 2px rgba(255,179,71,.4); }
.tree-node.leaf.active > .tree-leaf-link .leaf-dot { background:#ff8c1a; opacity:1; transform:scale(1.4); box-shadow:0 0 0 1px rgba(255,140,26,.55), 0 0 6px 2px rgba(255,140,26,.35); }
/* Root list expand/collapse transition overrides for smooth fade+slide */
.menu-fade-slide-enter-active, .menu-fade-slide-leave-active { overflow:hidden; }
.menu-fade-slide-enter-from, .menu-fade-slide-leave-to { opacity:0; }
@media (prefers-reduced-motion: reduce) { .tree-item, .tree-leaf-link { transition: background .2s, color .2s; } .caret-icon { transition:none; } }
@media (prefers-reduced-motion: reduce) { .nav-item { transition: background .2s, color .2s; } .nav-item::before, .nav-item::after { animation:none !important; transform:none !important; } }
.sidebar-foot { font-size: 12px; opacity: .75; padding: .5rem .5rem 0; }
.main-panel { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.page-topbar { background: #fff; padding: .85rem 1.3rem; border-bottom: 1px solid #e2e8f0; }
.page-title { margin: 0; font-size: 18px; font-weight: 600; display: flex; align-items: center; gap: .6rem; }
.page-logo { width: 28px; height: 28px; object-fit: contain; background:#fff; border-radius:6px; padding:2px; box-shadow:0 1px 3px rgba(0,0,0,.25); }
.view-wrapper { padding: 1rem 1.3rem 2rem; flex: 1; overflow: auto; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
</style>
